package com.dana1.nos.old.util;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;

public class PropertyUtils
{
	public static final PropertyDescriptor [] getPropertyDescriptors(final Class<?> beanClass, final Class<?> stopClass)
	{
		if (Proxy.isProxyClass(beanClass)) 
			return __getPropertyDescriptors(beanClass,stopClass); 
		else
			return HardMemoizer.memoize(new Producer<PropertyDescriptor []> () {
				@Override
				public PropertyDescriptor[] produce()
				{
					return __getPropertyDescriptors(beanClass,stopClass);
				}
			},PropertyUtils.class,beanClass,stopClass);
	}
	
	public static final PropertyDescriptor getPropertyDescriptor(final Class<?> beanClass, final Class<?> stopClass, final String propertyName) 
	{
		if (Proxy.isProxyClass(beanClass))
			return __getPropertyDescriptor(beanClass, stopClass, propertyName);
		else
			return HardMemoizer.memoize(new Producer<PropertyDescriptor> () {
				@Override
				public PropertyDescriptor produce()
				{
					return __getPropertyDescriptor(beanClass, stopClass, propertyName);
				}
			},PropertyUtils.class,beanClass,stopClass,propertyName);
	}


	private static PropertyDescriptor __getPropertyDescriptor(Class <?> beanClass, Class<?> stopClass, String propertyName)
	{
		for (PropertyDescriptor p: getPropertyDescriptors(beanClass,stopClass))
			if (p.getName().equals(propertyName))
				return p;
		throw new IllegalArgumentException(propertyName+" not found");
	}
	
	private static PropertyDescriptor[] __getPropertyDescriptors(Class<?> beanClass,Class<?> stopClass)
	{
		try
		{
			return Introspector.getBeanInfo(maybeConvertInferfaceClass(beanClass),stopClass).getPropertyDescriptors();
		}
		catch (IntrospectionException e)
		{
			throw new RuntimeException(e.getMessage(),e);
		}
	}

	@SuppressWarnings("unchecked")
	private static Class<?> maybeConvertInferfaceClass(Class<?> beanClass)
	{
		if (beanClass.isInterface())
		{
			Class [] allInterfaceNames = (Class []) ClassUtils.getAllInterfaces(beanClass).toArray(new Class[0]);
			allInterfaceNames = (Class [])ArrayUtils.add(allInterfaceNames, 0, beanClass);
			beanClass = Proxy.getProxyClass(beanClass.getClassLoader(), allInterfaceNames);
		}
		return beanClass;
	}

	public static final Method getInterfaceMethod(Class<?> objectClass, PropertyDescriptor propDesc)
	{
		try
		{
			return objectClass.getMethod(propDesc.getReadMethod().getName(),new Class<?>[0]);
		}
		catch (Exception e)
		{
			if (e instanceof RuntimeException)
				throw (RuntimeException)e;
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	
	public static final boolean readMethodMatches(PropertyDescriptor propDesc, String name)
	{
		return (null!=propDesc.getReadMethod())&&propDesc.getReadMethod().getName().equals(name);
	}
}
